using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using Charisma.Common.Disposable;

namespace Charisma.Linq
{
  [Serializable]
  [DebuggerDisplay("Count = {Count}")]
  public class BindingCollection<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
    where TKey : Expression
    where TValue : Expression
  {
    [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
    protected readonly Dictionary<TKey, TValue> bindings = new Dictionary<TKey, TValue>();
    protected readonly HashSet<TKey> permanentBindings = new HashSet<TKey>();

    public virtual int Count
    {
      [DebuggerStepThrough]
      get { return bindings.Count; }
    }

    public virtual TValue this[TKey key]
    {
      [DebuggerStepThrough]
      get { return bindings[key]; }
    }

    public virtual Disposable Add(TKey key, TValue value)
    {
      TValue previous;
      if (bindings.TryGetValue(key, out previous)) {
        bindings[key] = value;
        return new Disposable(_ => {
            if (!permanentBindings.Contains(key))
              bindings[key] = previous;
          });
      }
      bindings.Add(key, value);
      return new Disposable(_ => {
          if (!permanentBindings.Contains(key))
            bindings.Remove(key);
        });
    }

    public virtual Disposable PermanentAdd(TKey key, TValue value)
    {
      bindings[key] = value;
      if (!permanentBindings.Contains(key))
        permanentBindings.Add(key);
      return null;
    }

    public virtual void ReplaceBound(TKey key, TValue value)
    {
      if (!bindings.ContainsKey(key))
        throw new KeyNotFoundException();
      bindings[key] = value;
    }

    [DebuggerStepThrough]
    public virtual bool TryGetValue(TKey key, out TValue value)
    {
      return bindings.TryGetValue(key, out value);
    }

    public virtual IEnumerable GetKeys()
    {
      return bindings.Keys;
    }

    #region IEnumerable<...> methods

    /// <inheritdoc/>
    public virtual IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
    {
      return bindings.GetEnumerator();
    }

    /// <inheritdoc/>
    IEnumerator IEnumerable.GetEnumerator()
    {
      return GetEnumerator();
    }

    #endregion
  }
}